Explained: Comunicating 2 scripts with custom window Messages.


Sometimes we had two Scripts and we want to communicate both of them, sending values or just to execute some function on the second script when the first script detect some condition.

Everything starts Registering on Windows OS a new custom message:
[AutoIt]
#include <WinAPISysWin.au3> 

Global $wmUserMsg= _WinAPI_RegisterWindowMessage('my_custom_Window_Message')
[/AutoIt]

This winApi takes a string and return an integer Identifier for it. The string itself could be anything you like. Since now we only use $wmUserMsg variable, We won't use the string anymore.
If you send the same exact string, RegisterWindowMessage will always return the same number, the same identifier.
You need to do this on both scripts you want to send this message and the one that receives that message.

now the sender Script could just create and send the message to the second script (that need to be running in order to receive this message)

[AutoIt]
#include <SendMessage.au3>  

If WinExists("Second Script") Then
	local $hSecond = WinGetHandle("Second Script")
	if not @error Then
		_SendMessage( $hSecond, $wmUserMsg)
	Else
		ConsoleWrite("Second Script doesn't exists. Did you close it?" & @CRLF)
	EndIf
EndIf
[/AutoIt]
$hSecond is the handle of the window that will receive the message. Since we are using windows Messages, we need a window, and therefore WindowsOS create a function that handle all messages that a normal window receives (mouse move, mouse click, minimize window, restore...and our custom message too), this function appears on AutoIt helpFile as WNDPROC (WiNDows PROCedure. Procedure for us, it is just a Function) so we need to create a GUI on the second script to be able to receive the message. Gui can be hidden if you don't need it, and it will be found by "WinExists" 
$wmUserMsg is the identifier message already registered.

How the second script receives the Message?

[AutoIt]
Global $wmNotifyMe= _WinAPI_RegisterWindowMessage('my_custom_Window_Message') ; the same exact string than First Script
GUIRegisterMsg($wmNotifyMe, WM_NotifyMe)
[/AutoIt]

The string on Winapi parameter must be the same, but returned value is just a number and here I called the variable as $wmNotifyMe (it could be called $wmUserMsg too but because we are on the second Script I named differently just to avoid confussions)

GUIRegisterMsg says that whenever an $wmNotifyMe is received, a function named WM_NotifyMe must be called.
If you look helpfile, the definition of this function is fixed and we must NOT change the number of parameters. It will looks like this:

[AutoIt]
Func WM_NotifyMe($hWndGUI, $MsgID, $WParam, $LParam)
	If $MsgID == $wmNotifyMe Then
		; Received a custom message, the only application on Windows that Sends this message is 
		; the first script I made.  So do here Whatever you need.
		; GUIRegisterMsg helpfile says don't block this function, so don't use MessageBox, sleep, etc here!
		DoStuff()
		
	EndIf
	Return $GUI_RUNDEFMSG
EndFunc

[/AutoIt]
You can change the name of the function whatever you like, I suggest to keep the  "WM_" prefix to easy understand that it is a Windows Message Handler.

Example01 folder of the zip, ilustrate this example. Open and run Example01/First.au3 and wait 5 seconds, after that you would see Sending and receiving a Windows Message.

Done. Was it complicated? In summary:
- Register a Message
- Sends it
- Receive it.
got it? Hope so.


There are two ways to Send a Message:
- _SendMessage function from <SendMessage.au3>
- _WinAPI_PostMessage from <WinAPISysWin.au3>

Both functions has the same parameter list.

_SendMessage call directly the WNDPROC and waits for a response or until WNDPROC finish its tasks about this message.
PostMessage put the message on the WNDPROC queue and just return inmediatly. Window that receives this message need to process all pending messages on the queue before processing our message.

Pros and cons:
- If you plan that First script send a Message to the second and the second script send a message to the First... Using SendMessage you could create and endless loop, and you are blocking both WNDPROC. So it is safer to use PostMessage here.

It is said that SendMessage are commonly used for critic messages, for example, low battery level, computer must be shutdown inmediatly, Send a Message to all applications and they have the ability to refuse if you will loose work if that application is automatically closed.

If the message is not critic, use PostMessage, it's the way Windows OS works.  For example: First script send a message to the second that means " hey!, I saved a ini File and now you can read that file to update your configuration." There is no hurry here. If this message is on the second script WNDPROC queue waiting to be processed and you manually close the Second script, nothing happens, It doesn't matter right?. Next time you open Second script will read its refreshed ini file and everything works.

Can we send some values to the second script?
[AutoIt]
Func WM_NotifyMe($hWndGUI, $MsgID, $WParam, $LParam)
[/AutoIt]

We have two parameters called WParam and LParam, we could send integer values on it (they are defined on Windows as Long Int, also known as Int32 or Integers with 32 bits wide). 
That is not really true, WParam depend on your platform, it could be 32 bits wide or 64 bits wide. I suppose it will be 64 bits if you are using 64 bits hardware, WinOS 64 bits and AutoIt64 bits.
If you plan to send different integers values, I suggest to create a different file with constants that will be imported on both scripts you are creating. Just the same you do with AutoIts includes:
[AutoIt]
#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>
#include "MyConstantMessages.au3"  ; double quotes because I place this file on the same folder than First.au3 and Second.au3 files.
[/AutoIt]

Example02 folder ilustrate this example. This is the way WindowsOS do it, I mean Windows Create a Message Constant and then use WParam and LParam to send values of this Message. You need to search info of the Message to know how to interpret WParam and LParam  (MSDN).
- Sometimes WParam is just an integer value
- Sometimes WParam is a pointer to a function (callback). 

Please note that WParam and LParam are translated to Hexadecimal notation, so if you translate to integer to see the value with "ConsoleWrite", you would interpret that the pointer is just a number when it is not.

Example03 do the same than Example02 but instead of using parameter values, I create 2 different Windows Messages and register on the same function WM_NotifyMe.

At this point we need to talk a bit about Windows API Integer Datatypes

Name		number_of_bits		minimun_integer_value		maximum_integer_value		also_known_as
  bit		 1							0							         1
  byte		 8							0							       255			 
  Word		16							0							     65535 			16-bit unsigned integer
  DWord		32							0							4294967295 			32-bit unsigned integer, Double Word, LONG
  
Full list: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types

You need to know this table because if you want to send a integer value of... let's say 1000, you need at least a Word integer. If you try to use a byte, you will send the value of 1000 in your Sender Application, but the second script will always receives a maximum of 255 value or a lower value, because the number was truncated while creating the Windows message.

This can be controlled with _SendMessage Parameter Datatypes. I suggest you don't mess with them.

One trick is widely used on Windows Messages is to split a 32 bits into two numbers of 16 bits. Let's see an example:
I want the First script to send four integers values to the second Script, for example: Left, Top, Width and Height, so the first script decides the position and Dimension that the second script will have on Screen. Example04 folder shows the full code.

I use Windows Resolution of 1920x1080 so the maximum integer value that I will send is 1920, something that could be represented with 16 bits wide, so I need at least a "Word".

[AutoIt]
#include <WinAPISysWin.au3> ; _WinAPI_PostMessage
#include <WinAPIConv.au3> 	; _WinAPI_MakeLong
#include <SendMessage.au3>  ; _SendMessage


	local $iLeft = 1000
	local $iTop = 1000
	local $iWidth = 500
	local $iHeight = 300

	local $wparam = _WinAPI_MakeLong($iLeft, $iTop)   
	local $lparam =_WinAPI_MakeLong($iWidth, $iHeight)  
	;_SendMessage($hSecond, $wmUserMsg, $wparam, $lparam)  ; just choose one, _SendMessage or _WinAPI_PostMessage
	_WinAPI_PostMessage($hSecond, $wmUserMsg, $wparam, $lparam)
[/AutoIt]
_WinAPI_MakeLong takes two numbers of 16 bits and it encodes into a 32 bits. It is easy to understand at this way:
[code]
----------------------------------
|           32 bits wide         |
----------------------------------
|   16 bits     |   16 bits      |
----------------------------------
[/code]
The right 16 bits part is called Low Word  (LoWord) First parameter of _WinAPI_MakeLong
The left 16 bits part is called High Word  (HiWord) Second parameter of _WinAPI_MakeLong


The Left Coordenate will be hold on the Low Word.
The Top Coordenate will be hold on the High Word.

As I said, We are sending four integer values in two parameters of a function, cool right? the way to decode "MakeLong" on the second script is:
[AutoIt]
Func WMUSER_MSG($hWndGUI, $MsgID, $WParam, $LParam)
	; $WParam and $LParam are Long Int params
	If $MsgID == $wmUserMsg Then
		Local $iLeft   = _WinAPI_LoWord($WParam)
		Local $iTop    = _WinAPI_HiWord($WParam)
		
		Local $iWidth  = _WinAPI_LoWord($LParam)
		Local $iHeight = _WinAPI_HiWord($LParam)
		
		WinMove($hGui2, "", $iLeft, $iTop, $iWidth, $iHeight)
		; $hGui2 is the window of the second script and the first script told me the Position and Dimension I should adopt.
		GUISetState(@SW_SHOW)
	EndIf
EndFunc
[/AutoIt]

What about sending a string of chars?. Well it's easy to create an array where the WParam is the index of the string we want to send, so we still send an integer value instead of a full string. Example05 shows full code. There are lots of ways to do the same thing, just use your imagination.

I'm sure someone could say: "On the First script I could use WinMove to change position and Dimension of the second script". Right! but you are controlling the second script, you are not communicating scripts.